/**
 *    Copyright (C) 2007-2009 DIVIS GmbH (http://www.divis-hagen.de/)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 3 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General
 *    Public License along with this library; if not, write to the
 *    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *    Boston, MA 02110-1301 USA
 */

package de.divis_hagen.json.rs.client;

import de.divis_hagen.json.annotation.EntityRequest;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;


/**
 *
 * @author Aike J Sommer
 */
public class RequestInfo {

    public static enum Method {
        GET, POST
    }
    
    public static RequestInfo find(EntityRequest.Type type, EntityInfo entity) {
        for (java.lang.reflect.Method m : entity.getHandler().getMethods()) {
            if (m.isAnnotationPresent(EntityRequest.class) &&
                    m.getAnnotation(EntityRequest.class).value() == type) {
                String path = entity.getRootPath() + m.getAnnotation(Path.class).value();
                Method method = findMethod(m);
                Map<String, Class> params = findParams(m);

                return new RequestInfo(entity, path, method, params);
            }
        }

        return null;
    }
    
    private static Method findMethod(java.lang.reflect.Method m) {
        if (m.isAnnotationPresent(GET.class)) {
            return Method.GET;
        }
        if (m.isAnnotationPresent(POST.class)) {
            return Method.POST;
        }

        throw new IllegalArgumentException(m.toString());
    }

    private static Map<String, Class> findParams(java.lang.reflect.Method m) {
        Map<String, Class> params = new LinkedHashMap<String, Class>();

        Annotation[][] allAnnotations = m.getParameterAnnotations();
        Class[] types = m.getParameterTypes();
        for (int i = 0; i < types.length; i++) {
            Class type = types[i];
            Annotation[] annotations = allAnnotations[i];

            for (int j = 0; j < annotations.length; j++) {
                Annotation a = annotations[j];
                if (a.annotationType().equals(PathParam.class)) {
                    String name = ((PathParam) a).value();
                    if (! params.containsKey(name)) {
                        params.put(name, type);
                    }
                    break;
                }
            }
        }

        return params;
    }

    private final EntityInfo entity;
    private final String path;
    private final Method method;
    private final Map<String, Class> params;

    public RequestInfo(EntityInfo entity, String path, Method method,
            Map<String, Class> params) {
        this.entity = entity;
        this.path = path;
        this.method = method;
        this.params = params;
    }

    public String getJSPath() {
        StringBuilder b = new StringBuilder("\"");
        boolean inParam = false;
        boolean inPattern = false;
        for (int i = 0; i < path.length(); i++) {
            char c = path.charAt(i);
            if (inParam) {
                if (c == '}') {
                    inParam = false;
                    b.append(" + \"");
                } else if (! inPattern) {
                    if (c == ':') {
                        inPattern = true;
                    } else {
                        b.append(c);
                    }
                }
            } else {
                if (c == '{') {
                    inParam = true;
                    inPattern = false;
                    b.append("\" + ");
                } else {
                    b.append(c);
                }
            }
        }
        b.append("\"");
        return b.toString();
    }

    public Method getMethod() {
        return method;
    }

    public Map<String, String> getJSParams() {
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (Map.Entry<String, Class> param : params.entrySet()) {
            if (Long.TYPE.isAssignableFrom(param.getValue())) {
                result.put(param.getKey(), "Number");
            } else {
                throw new IllegalArgumentException(param.getValue().getName());
            }
        }

        return result;
    }

}
